package main

import (
	"fmt"
	"strconv"
	"sync"
	"time"
)

var (
	getChan chan string
	setChan chan string
)

func main() {
	fmt.Println("初始化缓存")
	/*
		第一个 参数 为默认 过期时间
		第二个 参数 为清理缓存数据定时器的时间
	*/
	getChan = make(chan string, 4)
	setChan = make(chan string, 4)
	c := NewCache(2*time.Second, 2*time.Second)
	cacheName := "cahce:"
	// 写缓存
	go func() {

		for i := 0; i < 30; i++ {
			c.Set(cacheName+strconv.Itoa(i), i, 5)
		}
	}()

	go func() {
		for i := 0; i < 30; i++ {
			c.Set(cacheName+strconv.Itoa(i), i, 5)
		}
	}()

	// 读缓存
	go func() {
		for i := 0; i < 30; i++ {
			result, a := c.Get(cacheName + strconv.Itoa(i))
			fmt.Println("返回数据A", result, a)
		}
	}()
	// 读缓存
	go func() {
		for i := 0; i < 30; i++ {
			result, a := c.Get(cacheName + strconv.Itoa(i))
			fmt.Println("返回数据B", result, a)
		}
	}()
	time.Sleep(20 * time.Second)
}

// 设置缓存数据项，如果数据项存在则覆盖
func (c *Cache) Set(k string, v interface{}, d time.Duration) {
	var e int64
	if d == 0 {
		d = c.defaultExpiration
	} else {
		// 设置为秒
		d = d * time.Second
	}
	if d > 0 {
		e = time.Now().Add(d).UnixNano()
	}
	//select {
	//case  <-getChan:
	//	//fmt.Println("正在读取Map")
	//	return
	//default:
	//	//使用 chan 有问题，先这样吧~~~~~
	//	setChan <- "正在写入"
	//	fmt.Println("执行到这哭可")
	//
	//	break
	//}

	// 设置为 写入状态 不允许读取
	c.mu.Lock()
	defer c.mu.Unlock()
	c.items[k] = Item{
		Object:     v,
		Expiration: e,
	}
}

// 判断数据项是否已经过期
func (item Item) Expired() bool {
	if item.Expiration == 0 {
		return false
	}
	// 当前时间 是否 大于设置的时间
	return time.Now().UnixNano() > item.Expiration
}

// 获取数据项
func (c *Cache) Get(k string) (interface{}, bool) {

	//select {
	//// 使用 chan 有问题，先这样吧~~~~~
	//case  <-setChan:
	//	//fmt.Println("正在写入Map")
	//	return nil,false
	//default:
	//	getChan <- "正在读取"
	//	break
	//}

	// 设置为读取状态 禁止写入
	c.mu.RLock()
	item, found := c.items[k]
	if !found {
		c.mu.RUnlock()
		return nil, false
	}
	// 判断是否过期
	if item.Expired() {
		return nil, false
	}
	// 取消读取状态 允许写入
	c.mu.RUnlock()
	return item.Object, true
}

type Item struct {
	Object     interface{} // 真正的数据项
	Expiration int64       // 生存时间
}

type Cache struct {
	defaultExpiration time.Duration
	items             map[string]Item // 缓存数据项存储在 map 中
	mu                sync.RWMutex    // 读写锁
	gcInterval        time.Duration   // 过期数据项清理周期
	stopGc            chan bool
}

// 创建一个缓存系统
func NewCache(defaultExpiration, gcInterval time.Duration) *Cache {
	c := &Cache{
		defaultExpiration: defaultExpiration,
		gcInterval:        gcInterval,
		items:             map[string]Item{},
		stopGc:            make(chan bool),
	}
	// 开始启动过期清理 goroutine
	go c.gcLoop()
	return c
}

// 过期缓存数据项清理
func (c *Cache) gcLoop() {
	// 设置一个时间定时器
	ticker := time.NewTicker(c.gcInterval)
	for {
		select {
		case <-ticker.C:
			// 删除过期数据
			c.DeleteExpired()
			fmt.Println("执行过期数据A")
		case <-c.stopGc:
			fmt.Println("执行一次B")
			ticker.Stop()
			return
		}
	}
}

// 删除过期数据项
func (c *Cache) DeleteExpired() {
	now := time.Now().UnixNano()
	c.mu.Lock()
	defer c.mu.Unlock()

	for k, v := range c.items {
		if v.Expiration > 0 && now > v.Expiration {
			c.delete(k)
		}
	}
}

// 删除缓存数据项
func (c *Cache) delete(k string) {
	delete(c.items, k)
}
